///////////////
//  by 辜勇胜 //
///////////////
#include "web_server.h"
#include "esp_http_server.h"
#include "Arduino.h"
#include "step_motor.h"
#include "esp_camera.h"
#include "motor.h"


#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";

// 控制器服务器句柄
httpd_handle_t web_httpd = NULL;
// 视频流服务器句柄
httpd_handle_t stream_httpd = NULL;

// index页面处理器
static esp_err_t index_handler(httpd_req_t *req);
// 控制命令处理器
static esp_err_t control_handler(httpd_req_t *req);
// 视频流处理器
static esp_err_t stream_handler(httpd_req_t *req);

// 开启web服务器
void startServer() {
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();

  httpd_uri_t index_uri = {
    .uri = "/",
    .method = HTTP_GET,
    .handler = index_handler,
    .user_ctx = NULL
  };

  httpd_uri_t control_uri = {
    .uri = "/control",
    .method = HTTP_GET,
    .handler = control_handler,
    .user_ctx = NULL
  };

  httpd_uri_t stream_uri = {
    .uri = "/stream",
    .method = HTTP_GET,
    .handler = stream_handler,
    .user_ctx = NULL
  };

  // 初始化服务器
  Serial.printf("Starting web server on port: '%d'\n", config.server_port);
  if (httpd_start(&web_httpd, &config) == ESP_OK) {
    // 注册handler
    httpd_register_uri_handler(web_httpd, &index_uri);
    httpd_register_uri_handler(web_httpd, &control_uri);
  }

  config.server_port += 1;
  config.ctrl_port += 1;
  if (httpd_start(&stream_httpd, &config) == ESP_OK) {
    // 注册视频流handler
    httpd_register_uri_handler(stream_httpd, &stream_uri);
  }
}

// index页面处理器
static esp_err_t index_handler(httpd_req_t *req) {
  //httpd_resp_set_type(req, "text/html");
  //httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
  //return httpd_resp_send(req, (const char *)index_ov2640_html_gz, index_ov2640_html_gz_len);
}
// 控制命令处理器
static esp_err_t control_handler(httpd_req_t *req) {
  char*  buf;
  size_t buf_len;
  char cmd[32] = {0,};
  buf_len = httpd_req_get_url_query_len(req) + 1;
  if (buf_len > 1) {
    buf = (char*)malloc(buf_len);
    if (!buf) {
      httpd_resp_send_500(req);
      return ESP_FAIL;
    }
    // 获取参数cmd
    if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
      if (httpd_query_key_value(buf, "cmd", cmd, sizeof(cmd)) == ESP_OK ) {

      } else {
        free(buf);
        httpd_resp_send_404(req);
        return ESP_FAIL;
      }
    } else {
      free(buf);
      httpd_resp_send_404(req);
      return ESP_FAIL;
    }

  } else {
    httpd_resp_send_404(req);
    return ESP_FAIL;
  }

  // 摄像头向上抬
  if (!strcmp(cmd, "up")) {
    Serial.println("up");
    motor_step_cw();
  }
  // 摄像头向下
  else if (!strcmp(cmd, "down")) {
     Serial.println("down");
    motor_step_ccw();
  }
  // 停止运动
  else if (!strcmp(cmd, "pause")) {
    motor_step_pause();
  }
  // 摄像头控制加速
  else if (!strcmp(cmd, "speedAdd")) {
    motor_step_add_speed();
  }
  // 摄像头控制减速
  else if (!strcmp(cmd, "speedSub")) {
    motor_step_sub_speed();
  } else if (!strcmp(cmd, "forward")) {
    // 前进
    car_forward();
  } else if (!strcmp(cmd, "left")) {
    // 左转
    car_left();
  } else if (!strcmp(cmd, "right")) {
    // 右转
    car_right();
  } else if (!strcmp(cmd, "stop")) {
    // 停止前进
    car_stop();
  } else {
    free(buf);
    return httpd_resp_send_500(req);
    return ESP_FAIL;
  }

  free(buf);
  httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  return httpd_resp_send(req, NULL, 0);
}

// 视频流处理器
static esp_err_t stream_handler(httpd_req_t *req) {

  camera_fb_t * fb = NULL;
  esp_err_t res = ESP_OK;
  size_t _jpg_buf_len = 0;
  uint8_t * _jpg_buf = NULL;
  char * part_buf[64];
  bool detected = false;
  res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
  if (res != ESP_OK) {
    return res;
  }
  httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");



  while (true) {
    detected = false;

    fb = esp_camera_fb_get();

    if (fb->format != PIXFORMAT_JPEG) {
      bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
      esp_camera_fb_return(fb);
      fb = NULL;
      if (!jpeg_converted) {
        Serial.println("JPEG compression failed");
        res = ESP_FAIL;
      }
    } else {
      _jpg_buf_len = fb->len;
      _jpg_buf = fb->buf;
    }


    if (res == ESP_OK) {
      res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
    }
    if (res == ESP_OK) {
      size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
      res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
    }
    if (res == ESP_OK) {
      res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
    }
    if (fb) {
      esp_camera_fb_return(fb);
      fb = NULL;
      _jpg_buf = NULL;
    } else if (_jpg_buf) {
      free(_jpg_buf);
      _jpg_buf = NULL;
    }
    if (res != ESP_OK) {
      break;
    }
  }

  return res;
}
